package com.bnpparibas.sfri.ant.classpath;

/*
 * Copyright 2004 Joey Gibson <joey@joeygibson.com>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.  
 */
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

/**
 * Creates an Ant Path-like structure based on the entries in an Eclipse
 * .classpath file.
 * 
 * @author jgibson
 * @version $Id: EclipseClasspathTask.java,v 1.1 2004/11/23 09:32:23 vthoule Exp $
 */
public class EclipseClasspathTask extends Task {
	public static final String DEFAULT_FILENAME = ".classpath";
	public static final String DEFAULT_PATHID = "classpath";
	public static final String DEFAULT_WORKSPACE = "c:/eclipse/workspace";

   public static final String MODE_ECLIPSE = "ECLIPSE";
   public static final String MODE_COPY = "COPY";
   public static final String MODE_PROPERTIES = "PROPERTIES";

//	private List prefFiles;

   /** Classpath file Path */
	private File dir ;
   /** Classpath Property Id to fill */
	private String pathId = DEFAULT_PATHID;
   /** Classpath file */
	private String fileName = DEFAULT_FILENAME;
   /** Verbose mode */
	private boolean verbose = false;
   /** Eclipse Workspace */
	private File workspace;

   /** Mode */
   private String mode = MODE_ECLIPSE;

   /** Classpath Property File */
   private File propertyFile;
   
   private Properties props = new Properties();
   private Map variables = null;  


   /**
    * @throws BuildException n/a
    */
   public void execute() throws BuildException {
      if (dir == null) {
         dir = getProject().getBaseDir();
      }
      if (propertyFile == null) {
         propertyFile = new File(dir, DEFAULT_FILENAME + ".properties");
      }
      

      if (mode.equalsIgnoreCase(MODE_COPY)) {
         log("About to copy [" + propertyFile.getAbsolutePath() + "]");
         // Get a mapping of library variables
         variables = getVariables();
         props.putAll(variables);
         try {
            props.store(new FileOutputStream(propertyFile),"");
			} catch (IOException ioe) {
            ioe.printStackTrace();
            throw new BuildException(ioe.getLocalizedMessage());
			}         
      } else if (mode.equalsIgnoreCase(MODE_PROPERTIES)) {
         try {
            props.load(new FileInputStream(propertyFile));
         } catch (IOException ioe) {
            ioe.printStackTrace();
            throw new BuildException(ioe.getLocalizedMessage());
         }        
         buildClasspath();
      } else if (mode.equalsIgnoreCase(MODE_ECLIPSE)) {
         // Get a mapping of library variables
         variables = getVariables();
         buildClasspath();
		} else {
         throw new BuildException("The Mode " + mode + " is invalid.\nSupported modes are " + MODE_ECLIPSE + "," + MODE_COPY + "," + MODE_PROPERTIES);
		}
   }
   
	/**
    * @throws BuildException n/a
	 */
   public void buildClasspath() throws BuildException {
      
      Document document = null;

      File inFile = null;

		/*
		 * Now create a file on the Eclipse classpath file (probably .classpath)
		 * and get a JDOM document from it.
		 */
		try {
			if (dir == null) {
				dir = getProject().getBaseDir();
			}

			inFile = new File(dir, fileName);

			SAXBuilder sax = new SAXBuilder();
			document = sax.build(inFile);
		} catch (FileNotFoundException e) {
			log("Unable to open Eclipse classpath file [" + inFile.getAbsolutePath() + "]");
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		if (document == null) {
			return;
		}

		// This is the Ant Path-like structure
		Path classPath = new Path(getProject());

		Element root = document.getRootElement();

		// Get all the elements that relate to the classpath and walk them
		Collection children = root.getChildren("classpathentry");

		if (children != null) {
			for (Iterator i = children.iterator(); i.hasNext();) {
				Element element = (Element) i.next();

				Attribute kind = element.getAttribute("kind");
				Attribute path = element.getAttribute("path");

				String location = null;

				/*
				 * If the "kind" attribute is either "var" or "lib" we can
				 * process it. The "lib" types are just copied straight from the
				 * attribute. The "var" types have to be mapped via the variable
				 * mapping. Any other types, we skip.
				 */
				if (kind.getValue().equals("var")) {
					String name = path.getValue();
					StringTokenizer st = new StringTokenizer(path.getValue(), "/");
					if (st.countTokens() > 0) {
						name = st.nextToken();
                  if (mode.equalsIgnoreCase(MODE_PROPERTIES)) {
                     location = (String) props.get(name);
                  } else {
                     location = (String) variables.get(name);
						}
                  while (st.hasMoreTokens()) {
                     location = location + "/" + st.nextToken();
                  }
					}

					if (location == null) {
						log("Unable to map variable [" + name + "]");
						continue;
					}

					if (verbose) {
						log("Adding " + location + ", (" + name + ")");
					}
				} else if (kind.getValue().equals("lib")) {
					location = path.getValue();

					if (verbose) {
						log("Adding " + location);
					}
				} else {
					if (verbose) {
						log("Skipping entry of type: [" + kind.getValue() + "]");
					}

					continue;
				}

				try {
					File file = new File(location);

					if (!file.isAbsolute()) {
                  if (kind.getValue().equals("var")) {
   						file = new File(dir, location);
                  } else if (kind.getValue().equals("lib")) {
                     file = new File(workspace, location);
                  }
   				}

					Path.PathElement e = classPath.createPathElement();
					e.setLocation(file);
				} catch (Exception e) {
					log("Error processing [" + location + "]: " + e.getMessage());
				}
			}
		}

		// Set the new path into the project
		getProject().addReference(pathId, classPath);

		if (verbose) {
			log("\n" + pathId + ": " + classPath.toString());
		}
	}

   /**
    * Find and parse the Eclipse prefs.ini file which contains the library
    * variables.
    * 
    * @return Map of variables to paths
    */
   protected Map getVariables() {
      // If no workspace was provided, use the default
      if (workspace == null) {
         workspace = new File(DEFAULT_WORKSPACE);
      }
      return EclipseClasspath.getVariables(workspace);
   }

   /**
    * Find and parse the Eclipse prefs.ini file which contains the library
    * variables.
    * 
    * @return Map of variables to paths
    */
   protected Map getVariables(final List _prefFiles) {
      // If no workspace was provided, use the default
      if (workspace == null) {
         workspace = new File(DEFAULT_WORKSPACE);
      }
      return EclipseClasspath.getVariables(workspace,_prefFiles);
   }

	/**
    * @param _directory Set the Directory where to search .classpath
	 */
   public void setDir(final File _directory) {
		this.dir = _directory;
	}

	/**
	 * @param _pathName Path Property Id to use to build classpath 
	 */
   public void setPathId(final String _pathName) {
		this.pathId = _pathName;
	}

	/**
	 * @param _fileName Classpath filename to analyse
	 */
   public void setFileName(final String _fileName) {
		this.fileName = _fileName;
	}

	/**
    * 
	 * @param _workspace Workspace Eclipse
	 */
   public void setWorkspace(final File _workspace) {
		this.workspace = _workspace;
	}

	/**
    * 
	 * @param _verbose True for verbose log (ANT Execution dependant)
	 */
   public void setVerbose(final boolean _verbose) {
		this.verbose = _verbose;
	}

	/**
	 * @param string
	 */
	public void setPropertyFile(final File _file) {
		propertyFile = _file;
	}

	/**
	 * @param string
	 */
	public void setMode(String string) {
		mode = string;
	}

}